package middleware

import (
	"bookkeeping-gin/app/common/response"
	"bookkeeping-gin/app/constant"
	"bookkeeping-gin/app/service"
	"bookkeeping-gin/global"
	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v5"
	"log"
	"strconv"
	"time"
)

func JwtAuth(guardName string) gin.HandlerFunc {
	return func(c *gin.Context) {
		// 客户端携带Token有三种方式 1.放在请求头 2.放在请求体 3.放在URI
		// 这里假设Token放在Header的Authorization中,并使用Bearer开头
		// Authorization:Bearer xxxxx.xxxx.xxxx
		// 这里的具体实现方式要依据你的实际业务情况决定
		authHeader := c.Request.Header.Get("Authorization")
		if authHeader == "" || len(authHeader) <= 7 || authHeader[:len(service.TokenType)] != service.TokenType {
			response.TokenFail(c)
			c.Abort()
			return
		}

		// Bearer xxxxx.xxxx.xxxx 截取出 Token
		tokenStr := authHeader[len(service.TokenType)+1:]
		token, err := jwt.ParseWithClaims(tokenStr, &service.CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
			return []byte(global.App.Config.Jwt.Secret), nil
		})
		// 是否在redis黑名单中
		if err != nil || service.JwtService.IsInBlacklist(tokenStr) {
			response.TokenFail(c)
			c.Abort()
			return
		}

		claims := token.Claims.(*service.CustomClaims)

		if claims.Issuer != guardName {
			response.TokenFail(c)
			c.Abort()
			return
		}

		// token 续签
		if claims.ExpiresAt.Unix()-time.Now().Unix() < global.App.Config.Jwt.RefreshGracePeriod {
			lock := global.Lock(constant.RefreshTokenLock, global.App.Config.Jwt.BlackListGracePeriod)

			if lock.Get() {
				err, user := service.JwtService.GetUserInfo(guardName, claims.ID)

				if err != nil {
					global.App.Log.Error(err.Error())
					lock.Release()
				} else {
					tokenData, _, _ := service.JwtService.CreateToken(guardName, user)
					c.Header("new-token", tokenData.AccessToken)
					c.Header("new-expires-in", strconv.Itoa(tokenData.ExpiresIn))
					_ = service.JwtService.JoinBlackList(token)
				}
			}
			return
		}

		c.Set("token", token)
		c.Set("userid", claims.ID)

		log.Println("token", token)
		log.Println("claims.ID", claims.ID)
		log.Println("claims.Subject", claims.Subject)
	}
}
